package com.example.day30_music_synthetic;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Description:
 * Created by ZhDu on 2021/1/4 14:01.
 */
@SuppressWarnings("deprecation")
public class MusicProcess {

    /**
     * 混合2个PCM
     *
     * @param pcm1Path 第一个pcm路径
     * @param pcm2Path 第二个pcm路径
     * @param toPath   生成的文件的路径
     * @param volume1  第一个pcm的音量
     * @param volume2  第二个pcm的音量
     */
    public static void mixPcm(String pcm1Path, String pcm2Path, String toPath, int volume1, int volume2) throws IOException {
        float vol1 = normalizeVolume(volume1);
        float vol2 = normalizeVolume(volume2);
        byte[] buffer1_pcm1 = new byte[2048];
        byte[] buffer2_pcm2 = new byte[2048];
        byte[] buffer3_out = new byte[2048];

        FileInputStream is1 = new FileInputStream(pcm1Path);
        FileInputStream is2 = new FileInputStream(pcm2Path);
        FileOutputStream fileOutputStream = new FileOutputStream(toPath);

        short temp1, temp2;
        int temp;
        boolean end1 = false, end2 = false;
        while (!end1 || !end2) {//2个都没结束
            if (!end1) {
                end1 = (is1.read(buffer1_pcm1) == -1);//判断是否把buffer1读取完
                //把音乐文件的数据读取到buffer3
                System.arraycopy(buffer1_pcm1,0,buffer3_out,0,buffer1_pcm1.length);
            }
            if (!end2) {
                end2 = (is2.read(buffer2_pcm2) == -1);//判断是否把buffer2读取完
                int voice = 0;//声音的值  跳过下一个声音的值    一个声音是2 个字节
                for (int i = 0; i < buffer2_pcm2.length; i+=2) {
                    //将buffer里音量的数值取出来
                    temp1 = (short) ((buffer1_pcm1[i] & 0xff) | (buffer1_pcm1[i + 1] & 0xff) << 8);
                    temp2 = (short) ((buffer2_pcm2[i] & 0xff) | (buffer2_pcm2[i + 1] & 0xff) << 8);
                    //进行音频合成
                    temp = (int) (temp1*vol1 + temp2*vol2);//音乐和 视频声音 各占一半
                    if (temp > 32767) {
                        temp = 32767;
                    }else if (temp < -32768) {
                        temp = -32768;
                    }
                    //将数值转化成pcm
                    buffer3_out[i]= (byte) (temp&0xff);
                    buffer3_out[i + 1] = (byte) ((temp >>> 8) & 0xFF);
                    fileOutputStream.write(buffer3_out);
                }
            }
            is1.close();
            is2.close();
            fileOutputStream.close();
        }
    }

    private static float normalizeVolume(int volume) {
        //把要设置的音量转化为float，保留精度
        return volume / 100f * 1;
    }

    @SuppressLint("WrongConstant")
    /**
     * @param startTimeUs 开始时间
     * @param endTimeUs 结束时间
     * @param videoVolume 视频音量
     * @param accVolume 音频音量
     */
    public void mixAudioTrack(Context context,String videoPath , String audioPath, String outPath,Integer startTimeUs,
            Integer endTimeUs,int videoVolume,int accVolume) throws Exception {
        final File videoPcmFile = new File(Environment.getExternalStorageDirectory(), "out1_video.pcm");
        final File musicPcmFile = new File(Environment.getExternalStorageDirectory(), "out2_music.pcm");
        decodeToPCM(videoPath,videoPcmFile.getAbsolutePath(),startTimeUs,endTimeUs);
        decodeToPCM(audioPath,musicPcmFile.getAbsolutePath(),startTimeUs,endTimeUs);
        final File mixPcmFile = new File(Environment.getExternalStorageDirectory(),"out3_mix.pcm");
        mixPcm(videoPcmFile.getAbsolutePath(),musicPcmFile.getAbsolutePath(),mixPcmFile.getAbsolutePath(),videoVolume,accVolume);
        new PcmToWavUtil(44100,AudioFormat.CHANNEL_IN_STEREO,2,AudioFormat.ENCODING_PCM_16BIT).pcmToWav(
                mixPcmFile.getAbsolutePath(),outPath
        );
    }

    @SuppressLint("WrongConstant")
    public void decodeToPCM(String musicPath, String outPath, int startTime, int endTime) throws Exception {
        if (endTime < startTime) {
            return;
        }
        //音频解封装  将MP3解成aac
        MediaExtractor mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(musicPath);
            int audioTrackIndex = selectTrack(mediaExtractor);
            mediaExtractor.selectTrack(audioTrackIndex);//将封装格式指向音频通道
            //跳到开始剪辑的位置
            mediaExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
            MediaFormat oriAudioFormat = mediaExtractor.getTrackFormat(audioTrackIndex);
            int maxBufferSize = 100 * 1000;//一次读取的最大字节数，100k
            if (oriAudioFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
                maxBufferSize = oriAudioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
            } else {
                maxBufferSize = 100 * 1000;
            }
            ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize);
            MediaCodec mediacodec = MediaCodec.createDecoderByType(oriAudioFormat.getString(MediaFormat.KEY_MIME));
            //设置解码信息 直接从音频文件获取
            mediacodec.configure(oriAudioFormat, null, null, 0);
            File pcmFile = new File(Environment.getExternalStorageDirectory(), "out.pcm");
            FileChannel writeChannel = new FileOutputStream(pcmFile).getChannel();
            mediacodec.start();
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            int outputBufferIndex = -1;
            while (true) {
                int decodeInputIndex = mediacodec.dequeueInputBuffer(100000);
                if (decodeInputIndex >= 0) {
                    //获取采样时间
                    long sampleTime = mediaExtractor.getSampleTime();
                    if (sampleTime == -1) {
                        break;
                    } else if (sampleTime < startTime) {
                        mediaExtractor.advance();//释放掉数据
                        continue;
                    } else if (sampleTime > endTime) {
                        break;
                    }
                    //获取到压缩数据
                    info.size = mediaExtractor.readSampleData(buffer, 0);
                    info.presentationTimeUs = sampleTime;
                    info.flags = mediaExtractor.getSampleFlags();

                    byte[] content = new byte[buffer.remaining()];
                    buffer.get(content);
//                    FileUtils.writeContent(content);//输出到文件
                    //解码
                    ByteBuffer inputBuffer = mediacodec.getInputBuffer(decodeInputIndex);
                    inputBuffer.put(content);
                    mediacodec.queueInputBuffer(decodeInputIndex, 0, info.size, info.presentationTimeUs, info.flags);
                    //释放上一帧的压缩数据
                    mediaExtractor.advance();
                }
                outputBufferIndex = mediacodec.dequeueOutputBuffer(info, 100_000);
                while (outputBufferIndex >= 0) {
                    ByteBuffer decodeOutputBuffer = mediacodec.getOutputBuffer(outputBufferIndex);
                    writeChannel.write(decodeOutputBuffer);//输出到out.pcm
                    mediacodec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediacodec.dequeueOutputBuffer(info, 100_000);
                }
            }
            writeChannel.close();
            mediaExtractor.release();
            mediacodec.stop();
            mediacodec.release();
            //转换MP3    pcm数据转换成mp3封装格式
//            File wavFile = new File(Environment.getExternalStorageDirectory(), "output.mp3");
//            new PcmToWavUtil(44100, AudioFormat.CHANNEL_IN_STEREO, 2,
//                    AudioFormat.ENCODING_PCM_16BIT).pcmToWav(pcmFile.getAbsolutePath(), wavFile.getAbsolutePath());
            Log.i("MusicProcess", "转换完毕...");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //一个音频文件里有很多个通道，找到音频通道
    private int selectTrack(MediaExtractor mediaExtractor) {
        //获取所有通道数
        int trackCount = mediaExtractor.getTrackCount();
        for (int i = 0; i < trackCount; i++) {
            MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
            //获取通道当前的格式
            String mime = trackFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                return i;
            }
        }
        return -1;
    }

}
